//
//  Hybrid.swift
//  mall-ios
//
//  Created by Meak Tsui on 2018/11/26.
//  Copyright © 2018 Meak Tsui. All rights reserved.
//

import Foundation
import SwiftyJSON
import WebKit
import Shared

let NATIVE_INTERFACE = "callNative"


public class MTHybrid: NSObject {
    public weak var vc:UIViewController?
    private weak var web:WKWebView?
    weak var uiDelegate:WKUIDelegate?
    public var config:MTHybridConfig
    public weak var naviDelegate:WKNavigationDelegate?
    public var isEnable:Bool = true
    public var isOpenWhiteList:Bool = false
    
    public init(_ config:MTHybridConfig = MTHybridConfig.default()) {
        self.config = config
        self.isOpenWhiteList = config.isOpenWhiteList
        self.isEnable = !self.isOpenWhiteList
        super.init()
        self.config.hybrid = self
    }
    
    public func disable() {
        self.config.producers.forEach {
            if let pd = $0.value as? StartAndStopAble {
                pd.stop()
            }
        }
    }
    
    public func bind(_ webview:WKWebView,finish: (() -> Void)? = nil) {
        self.web = webview
        webview.configuration.userContentController.add(self, name: NATIVE_INTERFACE)
        let build = Bundle.main.infoDictionary?["CFBundleVersion"] as! String
        let temp = UIWebView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height))
        let old = temp.stringByEvaluatingJavaScript(from: "navigator.userAgent") ?? ""
        webview.customUserAgent = old + " " + (self.config.custumerUA ?? "") + "/" + build
        webview.uiDelegate = self
        webview.navigationDelegate = self
    }
    
    public func on(ns:Ns,event:Api,params:[String:Any] = [:]) {
        if let producer = config.getProducer(ns: ns, action: event) {
            producer.produce(params: params)
        }
    }
    
    func callH5(event:String,params:[String:Any],complete: ((Any?,Error?) -> Void)? = nil) {
        if !Thread.isMainThread {
            DispatchQueue.main.async {
                self.callH5(event: event, params: params, complete: complete)
            }
            return
        }
        var paramStr = JSON(params).rawString() ?? "null"
        paramStr = transJsStr(paramStr)
        dPrint("##====================================================")
        dPrint("## time: \(Date())")
        dPrint("## event: \(event)")
        dPrint("## params: \(params)")
        dPrint("##====================================================")
        self.web?.evaluateJavaScript("window.eventDispatcher('\(event)','\(paramStr)');", completionHandler: complete)
    }
    
    
    /// 转换json字符串，变为javascript可解析的json
    ///
    /// - Parameter jsStr: swift语音中json字符串
    /// - Returns: js代码中可解析的json
    func transJsStr(_ jsStr:String) -> String{
        var returnStr = jsStr.replacingOccurrences(of: "\\", with: "\\\\")
        returnStr = returnStr.replacingOccurrences(of: "\"", with: "\\\"")
        returnStr = returnStr.replacingOccurrences(of: "\'", with: "\\\'")
        returnStr = returnStr.replacingOccurrences(of: "\n", with: "\\n")
        returnStr = returnStr.replacingOccurrences(of: "\r", with: "\\r")
        return returnStr
    }
    
}

extension MTHybrid: WKScriptMessageHandler {
    public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if !isEnable {
            dPrint("当前页面不允许使用hybrid功能")
            return
        }
        if message.name != NATIVE_INTERFACE {
            return
        }
        let obj = message.body as! [String: Any]
        var msg = MTHybridMsg()
        msg.params = obj
        msg.vc = self.vc
        msg.webview = self.web
        let json = JSON(message.body)
        let namespace = json["ns"].stringValue
        let action = json["action"].stringValue
        if let callbackId = json["callbackId"].string,let callbackFunction = json["callbackFunction"].string {
            msg.callbackId = callbackId
            msg.callbackFn = callbackFunction
            msg.callbackClosure = { [unowned self] result in
                var str = JSON(result).rawString() ?? "null"
                str = self.transJsStr(str)
                dPrint("## return: \(str)")
                DispatchQueue.main.async { [unowned self] in  //保证任何时候都在main线程中执行
                    self.web?.evaluateJavaScript("\(callbackFunction)('\(callbackId)','\(str)');", completionHandler: nil)
                }
            }
        }
        let handler = self.config.getHandler(ns: Ns(namespace), action: Api(action))
        dPrint("##====================================================")
        dPrint("## time: \(Date())")
        dPrint("## ns: \(namespace)")
        dPrint("## api: \(action)")
        dPrint("## params: \(obj)")
        dPrint("------------------------------------------------------")
        handler.handler(msg)
        dPrint("##====================================================")
    }
}

extension MTHybrid: WKUIDelegate {
    public func webView(_ webView: WKWebView, runJavaScriptTextInputPanelWithPrompt prompt: String, defaultText: String?, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (String?) -> Void) {
        if !isEnable {
            dPrint("当前页面不允许使用hybrid功能")
            completionHandler(nil)
            return
        }
        let json = JSON(parseJSON: prompt)
        let namespace = json["ns"].stringValue
        let action = json["action"].stringValue
        let handler = self.config.getHandler(ns: Ns(namespace), action: Api(action))
        var msg = MTHybridMsg()
        msg.vc = self.vc
        msg.webview = self.web
        msg.params = json.dictionaryObject!
        msg.callbackClosure = { (returnValue) in
            let resultStr = JSON(returnValue).rawString()
            completionHandler(resultStr)
        }
        dPrint("##====================================================")
        dPrint("## time: \(Date())")
        dPrint("## ns: \(namespace)")
        dPrint("## api: \(action)")
        dPrint("## params: \(prompt)")
        dPrint("------------------------------------------------------")
        handler.handler(msg)
        dPrint("##====================================================")
    }
    
    public func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        return uiDelegate?.webView?(webView, createWebViewWith: configuration, for: navigationAction, windowFeatures: windowFeatures)
    }
    
    public func webViewDidClose(_ webView: WKWebView) {
        uiDelegate?.webViewDidClose?(webView)
    }
    
    public func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
        if uiDelegate?.webView?(webView, runJavaScriptAlertPanelWithMessage: message, initiatedByFrame: frame, completionHandler: completionHandler) == nil {
            completionHandler()
        }
    }
    
    public func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        if uiDelegate?.webView?(webView, runJavaScriptConfirmPanelWithMessage: message, initiatedByFrame: frame, completionHandler: completionHandler) == nil {
            completionHandler(false)
        }
    }
}

extension MTHybrid: WKNavigationDelegate {
    public func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        if self.isOpenWhiteList {                       //判断当前页面是否在白名单内，不在不启用hybrid能力
            let host = navigationAction.request.url?.host
            for whiteHost in config.whiteUrlList {
                if host?.contains(whiteHost) ?? false {
                    self.isEnable = true
                }
            }
        }
        
        if self.naviDelegate?.webView?(webView, decidePolicyFor: navigationAction, decisionHandler: decisionHandler) == nil {
            decisionHandler(.allow)
        }
    }
    
    public func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse, decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        if self.naviDelegate?.webView?(webView, decidePolicyFor: navigationResponse, decisionHandler: decisionHandler) == nil {
            decisionHandler(.allow)
        }
    }
    
    public func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        naviDelegate?.webView?(webView, didStartProvisionalNavigation: navigation)
    }
    
    public func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!) {
        naviDelegate?.webView?(webView, didReceiveServerRedirectForProvisionalNavigation: navigation)
    }
    
    public func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
        naviDelegate?.webView?(webView, didFailProvisionalNavigation: navigation, withError:error)
    }
    
    public func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
        naviDelegate?.webView?(webView, didCommit: navigation)
    }
    
    public func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        naviDelegate?.webView?(webView, didFinish: navigation)
    }
    
    public func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        naviDelegate?.webView?(webView, didFail: navigation, withError: error)
    }
    
    public func webView(_ webView: WKWebView, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
        if self.naviDelegate?.webView?(webView, didReceive: challenge, completionHandler: completionHandler) == nil {
            completionHandler(.performDefaultHandling,nil)
        }
    }
    
    public func webViewWebContentProcessDidTerminate(_ webView: WKWebView) {
        naviDelegate?.webViewWebContentProcessDidTerminate?(webView)
    }
}
